跳到主要内容

21 内部FLASH读写实验

在这里插入图片描述

开发板官网https://www.edevkit.com/)

本系列教程目录:


内部 FLASH 读写实验

本章目标:

  • 了解 RA6M5 处理器的内部 FLASH 地址分布;
  • 学会使用 RASC 配置 FLASH 模块、使用 API 函数;

硬件设计

(略,参见配套硬件原理图)

软件设计

新建工程

复制一份“07 SCI_UART_Recv_send”修改文件夹名“21 Internal_Flash”,双击打开 EBF_RA6M5.uvprojx 工程,通过 Keil 的新建文件新建 flash.cflash.h,保存到目录下的 src 文件夹下。

FSP 配置

Keil 界面选择“Tools”→“RA Smart Configurator”打开 RASC 界面。

添加 FLASH HP Stack 模块。

插图01

插图02

Code Flash Programming Enable选择 Enabled,允许对 Code Flash 进行编程
Data Flash Programming Enable选择 Enabled,允许对 Data Flash 进行编程
Name设置模块名
Data Flash Background Operation设置是否启用 Data Flash 的后台操作功能
Callback设置后台操作的中断回调函数。这里设置为 flash_df_bgo_callback
Flash Ready Interrupt Priority设置 Flash 就绪中断优先级。这里设置为优先级 2
Flash Error Interrupt Priority设置 Flash 错误中断优先级。这里设置为优先级 2

配置完成之后可以按下快捷键“Ctrl + S”保存,最后点击“Generate Project Content”按钮,让软件自动生成配置代码即可。

程序设计

设计 Code Flash 读写测试程序,首先擦除指定块,检测块是否是空白块,是空白块则写入一组数据,接着读出这一组数据,对比读写数据是否相同,相同则 Flash 读写成功。

  1. 打开 Flash HP 设备
R_FLASH_HP_Open(flash_ctrl_t * const p_api_ctrl, flash_cfg_t const * const p_cfg);
  1. 擦除 Flash 块
R_FLASH_HP_Erase(flash_ctrl_t * const p_api_ctrl, uint32_t const address, uint32_t const num_blocks);

第二个参数指定 Flash 地址,第三个参数指定块数量。

  1. 检测块是否是空白块,也就是数据是否都是 0xFF。
R_FLASH_HP_BlankCheck(flash_ctrl_t * const p_api_ctrl,
uint32_t const address,
uint32_t num_bytes,
flash_result_t * blank_check_result);
  1. 写入数据
R_FLASH_HP_Write(flash_ctrl_t * const p_api_ctrl,
uint32_t const src_address,
uint32_t flash_address,
uint32_t const num_bytes);
  1. 读取数据
memcpy(read_buffer, (uint8_t *) CODE_FLASH_TEST_BLOCK, CODE_FLASH_TEST_DATA_SIZE);

读取数据只需通过取地址的方式就可以读取数据。

程序编写

flash_hp.h 头文件里定义初始化函数、读写测试函数。

#ifndef __BSP_INTERNAL_FLASH_H
#define __BSP_INTERNAL_FLASH_H
#include "hal_data.h"
#include "stdio.h"

#define CODE_FLASH_PRINTF(format, ...) printf("[Code FLASH Operation] "format"\r\n", ##__VA_ARGS__)

/* Code Flash */
#define FLASH_HP_CF_BLOCK_SIZE_32KB (32*1024) /* Block Size 32 KB */
#define FLASH_HP_CF_BLOCK_SIZE_8KB (8*1024) /* Block Size 8KB */

/* CODE FLASH 测试 */
#define CODE_FLASH_TEST_BLOCK 0x00100000U /* 避免覆盖程序代码数据 */
#define CODE_FLASH_TEST_DATA_SIZE 128 /* 只测试128个字节数据 */

/* 擦除的 FLASH 的块数 */
#define BLOCK_NUM 1

void flash_hp_Init(void);

void codeflash_ReadWrite_test(void);

#endif

flash_hp.c 里实现初始化函数、中断函数和读写函数。

#include "flash_hp.h"

//初始化flash
void flash_hp_Init(void)
{
fsp_err_t err = R_FLASH_HP_Open(&g_flash0_ctrl, &g_flash0_cfg);

if (FSP_SUCCESS != err)
{
printf("Flash_HP_Open API failed!\r\n");
printf("Returned Error Code: 0x%x\r\n", err);
while(1);
}

err = R_FLASH_HP_StartUpAreaSelect(&g_flash0_ctrl, FLASH_STARTUP_AREA_BLOCK0, true);
if (err != FSP_SUCCESS)
{
printf("Flash_HP_StartUpAreaSelect API failed\r\n");
printf("Returned Error Code: 0x%x\r\n", err);
while(1);
}
}

volatile bool flash_erase_complete_flag = false;
volatile bool flash_write_complete_flag = false;
volatile bool flash_event_not_blank = false;
volatile bool flash_event_blank = false;
volatile bool flash_event_error = false;
volatile flash_event_t flash_event_error_type;

// DATA FLASH 操作完成中断回调
void flash_hp_callback(flash_callback_args_t *p_args)
{
flash_event_t flash_event = p_args->event;

if (FLASH_EVENT_ERASE_COMPLETE == flash_event) //擦除完成
{
flash_erase_complete_flag = true;
}
else if (FLASH_EVENT_WRITE_COMPLETE == flash_event) //写入完成
{
flash_write_complete_flag = true;
}
else if (FLASH_EVENT_BLANK == flash_event)
{
flash_event_blank = true;
}
else if (FLASH_EVENT_NOT_BLANK == flash_event)
{
flash_event_not_blank = true;
}
else
{
flash_event_error = true;
flash_event_error_type = flash_event;
}
}

//读写测试函数
void codeflash_ReadWrite_test(void)
{
fsp_err_t err;
flash_result_t blank_check_result = FLASH_RESULT_BLANK;
uint8_t write_buffer[CODE_FLASH_TEST_DATA_SIZE] = {0};
uint8_t read_buffer[CODE_FLASH_TEST_DATA_SIZE] = {0};
uint8_t index;

// 写入测试数据到 write_buffer
for (index = 0; index < CODE_FLASH_TEST_DATA_SIZE; index++)
{
write_buffer[index] = (uint8_t) (index + 1);
}

CODE_FLASH_PRINTF("Erase Code Flash");

/* 禁用中断,以避免P/E过程中产生中断导致操作失败
注:这里禁用中断后,不能使用串口打印 */
__disable_irq();
err = R_FLASH_HP_Erase(&g_flash0_ctrl, CODE_FLASH_TEST_BLOCK, BLOCK_NUM);
__enable_irq();

if (FSP_SUCCESS != err)
{
CODE_FLASH_PRINTF("Erase API failed");
CODE_FLASH_PRINTF("Returned Error Code: 0x%x", err);
while(1);
}
CODE_FLASH_PRINTF("Erase successful");


/** 空白检查:擦除 Code Flash 完成后,进行空白检查
* 对 Code Flash 进行空白检查,并不需要在硬件层面(FCU)的操作,
* 实际上是用软件检查 Code Flash 指定区域的数据是否全为0xFF,
* 这与对 Data Flash 进行空白检查的情况是不同的。
*/
err = R_FLASH_HP_BlankCheck(&g_flash0_ctrl, CODE_FLASH_TEST_BLOCK, FLASH_HP_CF_BLOCK_SIZE_8KB, &blank_check_result);
/* Error Handle */
if (FSP_SUCCESS != err)
{
CODE_FLASH_PRINTF("BlankCheck API failed");
CODE_FLASH_PRINTF("Returned Error Code: 0x%x", err);
while(1);
}
CODE_FLASH_PRINTF("BlankCheck API Successful");

/* 验证空白检查的结果 */
if (FLASH_RESULT_BLANK == blank_check_result)
{
CODE_FLASH_PRINTF("The Flash block is blank.");
}
else if (FLASH_RESULT_NOT_BLANK == blank_check_result)
{
CODE_FLASH_PRINTF("The Flash block is not blank. Do not write data into it.");
while(1);
}

/** 读取 Code Flash 空白数据
* 可以不使用 R_FLASH_HP_BlankCheck 函数,自己来验证 Code Flash 区域是否空白
* 对于 Code Flash 来说,这段代码与调用 R_FLASH_HP_BlankCheck 函数的作用是一样的
*/
memcpy(read_buffer, (uint8_t *) CODE_FLASH_TEST_BLOCK, CODE_FLASH_TEST_DATA_SIZE);
CODE_FLASH_PRINTF("The %d bytes of data read into the read_buffer should all be 0xFF:", CODE_FLASH_TEST_DATA_SIZE);
for (index = 0; index < CODE_FLASH_TEST_DATA_SIZE; index++)
{
printf("%X ", read_buffer[index]);
}
printf("\r\nEnd.\r\n");


/* 写入测试数据到 Code Flash */
CODE_FLASH_PRINTF("Write flash data %d bytes:", CODE_FLASH_TEST_DATA_SIZE);
for (index = 0; index < CODE_FLASH_TEST_DATA_SIZE; index++)
{
printf("%d ", write_buffer[index]);
}
printf("\r\nEnd.\r\n");

__disable_irq();
err = R_FLASH_HP_Write(&g_flash0_ctrl, (uint32_t)write_buffer, CODE_FLASH_TEST_BLOCK, CODE_FLASH_TEST_DATA_SIZE);
__enable_irq();

if (FSP_SUCCESS != err)
{
CODE_FLASH_PRINTF("Write API failed");
CODE_FLASH_PRINTF("Returned Error Code: %d", err);
while(1);
}
CODE_FLASH_PRINTF("Writing flash data successful");


/* 读取 Code Flash 数据 */
memcpy(read_buffer, (uint8_t *) CODE_FLASH_TEST_BLOCK, CODE_FLASH_TEST_DATA_SIZE);
CODE_FLASH_PRINTF("read flash data %d bytes:", CODE_FLASH_TEST_DATA_SIZE);
for (index = 0; index < CODE_FLASH_TEST_DATA_SIZE; index++)
{
printf("%d ", read_buffer[index]);
}
printf("\r\nEnd.\r\n");


/* 对比 write_buffer 和 read_buffer 数据 */
if (0 == memcmp(read_buffer, write_buffer, CODE_FLASH_TEST_DATA_SIZE))
{
CODE_FLASH_PRINTF("Read and write data are the same, Code Flash successful test\r\n");
}
else
{
CODE_FLASH_PRINTF("Read and write data are not same, Code Flash failed test\r\n");
while(1);
}
}

hal_entry.c 文件中添加头文件 flash_hp.h,在 hal_entry 入口函数初始化,按键事件中执行测试函数。

void hal_entry(void)
{
/* TODO: add your own code here */

led_Init(); // LED 初始化
uart0_Init();
flash_hp_Init();

printf("START FLASH HP READ WRITE TEST \r\n");

while(1)
{
if(key_Scan(KEY1_PIN))
{
LED1_TOGGLE();

codeflash_ReadWrite_test();
}

}


#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}

下载验证

连接下载器,将程序编译并下载到开发板之后,按下复位按键来复位开发板,程序启动初始化完成后,观察实验结果。

按下按键执行 Flash 读写函数,首先擦除块检查是否是空白块,写入 1~128 共 128 个数据,然后再读出 128 个数据,对比读写数据是否相同,相同则测试成功。

插图03